home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / program / fpkpas92.zip / SRCRTL.ZIP / RTL / GETOPTS.PP next >
Text File  |  1997-07-01  |  11KB  |  414 lines

  1. unit getopts;
  2. {
  3.   --------------------------------------------------------------------
  4.   Getopt implementation for FPK pascal, modeled after GNU getopt.
  5.   Tested under Linux.
  6.   Tested under DOS
  7.   Michael Van Canneyt, 1997
  8.  
  9.   *NOTE*
  10.   The routines are a more or less straightforward conversion 
  11.   of the GNU C implementation of getopt. One day they should be 
  12.   replaced by some 'real pascal code'.
  13.   --------------------------------------------------------------------
  14. }
  15.  
  16. Interface
  17.  
  18. Const No_Argument       = 0;
  19.       Required_Argument = 1;
  20.       Optional_Argument = 2; 
  21.       EndOfOptions      = #255;
  22.     
  23. Type Option = Record
  24.        Name    : String;
  25.        Has_arg : Integer;
  26.        Flag    : ^char;
  27.        Value   : Char;
  28.       end;
  29.      POption  = ^Option;
  30.      Orderings = (require_order,permute,return_in_order);
  31.  
  32. Var OptArg : String;
  33.     OptInd : Integer;
  34.     OptErr : Boolean;
  35.     OptOpt : Char;
  36.       
  37. Function GetOpt (ShortOpts : String) : char;
  38. Function GetLongOpts (ShortOpts : String; 
  39.                       LongOpts : POption; 
  40.                       var Longind : Integer) : char;
  41.  
  42. Implementation
  43.  
  44. Var NextChar : integer;
  45.     first_nonopt,last_nonopt,Nrargs : Integer;
  46.     Ordering : orderings;
  47. {$ifndef linux}
  48.     argv : ^pchar;
  49. {$endif}
  50. { Copied straight from strings.pp, avoids the 'uses strings'  }
  51.  
  52. function strpas(p : pchar) : string;
  53.  
  54.       begin
  55.          asm
  56.             cld
  57.             movl 12(%ebp),%edi
  58.             movl %edi,%esi               
  59.             movl $0xffffffff,%ecx        
  60.             xorb %al,%al
  61.             repne
  62.             scasb
  63.             notl %ecx
  64.             decl %ecx
  65.             movl 8(%ebp),%edi          
  66.             movb %cl,%al
  67.             stosb
  68.             rep                         
  69.             movsb                       
  70.          end ['ECX','EAX','ESI','EDI'];
  71.       end;
  72.  
  73.     
  74. Procedure Exchange;
  75.  
  76. var bottom,middle,top,i,len : integer;
  77.     temp : pchar;
  78.  
  79. begin
  80.   bottom:=first_nonopt;
  81.   middle:=last_nonopt;
  82.   top:=optind;
  83.   while (top>middle) and (middle>bottom) do
  84.     begin
  85.     if (top-middle>middle-bottom) then
  86.       begin
  87.       len:=middle-bottom;
  88.       for i:=1 to len-1 do
  89.         begin
  90.         temp:=argv[bottom+i];
  91.         argv[bottom+i]:=argv[top-(middle-bottom)+i];
  92.         argv[top-(middle-bottom)+i]:=temp;
  93.         end;
  94.       top:=top-len;
  95.       end
  96.     else
  97.       begin
  98.       len:=top-middle;
  99.       for i:=0 to len-1 do
  100.         begin
  101.         temp:=argv[bottom+i];
  102.         argv[bottom+i]:=argv[middle+i];
  103.         argv[middle+i]:=temp;
  104.         end;
  105.       bottom:=bottom+len;
  106.       end;
  107.     end;
  108.   first_nonopt:=first_nonopt + optind-last_nonopt;
  109.   last_nonopt:=optind;
  110. end; { exchange }
  111.  
  112. procedure getopt_init (var opts : string);
  113.  
  114. begin
  115.   { Initialize some defaults. }
  116.   Optarg:='';
  117.   Optind:=1;
  118.   First_nonopt:=1;
  119.   Last_nonopt:=1;
  120.   OptOpt:='?';
  121.   Nextchar:=0;
  122.   if opts[1]='-' then 
  123.     begin
  124.     ordering:=return_in_order;
  125.     delete(opts,1,1);
  126.     end
  127.   else if opts[1]='+' then
  128.     begin
  129.     ordering:=require_order;
  130.     delete(opts,1,1);
  131.     end
  132.   else ordering:=permute;
  133. end;
  134.       
  135. Function Internal_getopt (Var Optstring : string;
  136.                           LongOpts : POption;
  137.                           LongInd : pointer;
  138.                           Long_only : boolean ) : char;
  139.  
  140. type pint=^integer; 
  141.  
  142. var temp,endopt,option_index : byte;
  143.     indfound: integer;
  144.     currentarg,optname : string;
  145.     p,pfound : POption;
  146.     exact,ambig : boolean;
  147.     c : char; 
  148.     
  149. begin
  150. optarg:='';
  151. if optind=0 then getopt_init(optstring);
  152. { Check if We need the next argument. }
  153. if optind<nrargs then currentarg:=strpas(argv[optind]) else currentarg:='';
  154. if (nextchar=0) then 
  155.   begin
  156.   if ordering=permute then
  157.     begin
  158.     { If we processed options following non-options : exchange }
  159.     if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  160.       exchange
  161.     else
  162.       if last_nonopt<>optind then first_nonopt:=optind;
  163.     while (optind<nrargs) and ((argv[optind][0]<>'-') 
  164.                                 or (length(strpas(argv[optind]))=1)) do
  165.       begin
  166.       inc(optind);
  167.       end;
  168.     last_nonopt:=optind;
  169.     end;
  170.   { Check for '--' argument }
  171.   if optind<nrargs then currentarg:=strpas(argv[optind]) else currentarg:='';
  172.   if (optind<>nrargs) and (currentarg='--') then
  173.     begin
  174.     inc(optind);
  175.     if (first_nonopt<>last_nonopt) and (last_nonopt<>optind) then
  176.       exchange
  177.     else 
  178.       if first_nonopt=last_nonopt then first_nonopt:=optind;
  179.     last_nonopt:=nrargs;
  180.     optind:=nrargs;
  181.     end; 
  182.   { Are we at the end of all arguments ? }
  183.   if optind>=nrargs then
  184.     begin
  185.     if first_nonopt<>last_nonopt then
  186.       optind:=first_nonopt;
  187.     Internal_getopt:=EndOfOptions;
  188.     exit;
  189.     end;
  190.   if optind<nrargs then currentarg:=strpas(argv[optind]) else currentarg:='';
  191.   { Are we at a non-option ? }
  192.   if (currentarg[1]<>'-') or (currentarg='-') then
  193.     begin
  194.     if ordering=require_order then
  195.        begin
  196.        Internal_getopt:=EndOfOptions;
  197.        exit;
  198.        end
  199.     else
  200.        begin
  201.        optarg:=strpas(argv[optind]);
  202.        inc(optind);
  203.        Internal_getopt:=#1;
  204.        exit;
  205.        end;
  206.     end;
  207.   { At this point we're at an option ...}
  208.   nextchar:=2;
  209.   if (longopts<>nil) and (currentarg[2]='-') then inc(nextchar);
  210.   { So, now nextchar points at the first character of an option }
  211.   end;
  212. { Check if we have a long option }
  213. if longopts<>nil then 
  214.   if length(currentarg)>1 then
  215.   if (currentarg[2]='-') or
  216.     ((not long_only) and (pos(currentarg[2],optstring)<>0)) then
  217.     begin
  218.     { Get option name }
  219.     endopt:=pos('=',currentarg);
  220.     if endopt=0 then endopt:=length(currentarg)+1;
  221.     optname:=copy(currentarg,nextchar,endopt-nextchar);
  222.     { Match partial or full }
  223.     p:=longopts;
  224.     pfound:=nil;
  225.     exact:=false;
  226.     ambig:=false;
  227.     option_index:=0;
  228.     indfound:=0;
  229.     while (p^.name<>'') and (not exact) do
  230.       begin
  231.       if pos(optname,p^.name)<>0 then
  232.         begin
  233.         if length(optname)=length(p^.name) then
  234.           begin
  235.           exact:=true;
  236.           pfound:=p;
  237.           indfound:=option_index;
  238.           end
  239.         else
  240.           if pfound=nil then
  241.             begin
  242.             indfound:=option_index;
  243.             pfound:=p
  244.             end
  245.           else
  246.             ambig:=true;
  247.         end;
  248.       inc (longint(p),sizeof(option));
  249.       inc (option_index);
  250.       end;
  251.     if ambig and not exact then 
  252.       begin
  253.       if opterr then
  254.          writeln (paramstr(0),': option "',optname,'" is ambiguous');
  255.       nextchar:=0;
  256.       inc(optind);
  257.       Internal_getopt:='?';
  258.       end;
  259.     if pfound<>nil then
  260.       begin
  261.       inc(optind);
  262.       if endopt<=length(currentarg) then
  263.         begin
  264.         if pfound^.has_arg>0 then
  265.           begin
  266.           optarg:=copy(currentarg,endopt+1,length(currentarg)-endopt);
  267.           end
  268.         else
  269.           begin
  270.           if opterr then
  271.             if currentarg[2]='-' then
  272.               writeln (paramstr(0),': option "--',pfound^.name,'" doesn''t allow an argument')
  273.             else  
  274.               writeln (paramstr(0),': option "',currentarg[1],pfound^.name,'" doesn''t allow an argument');
  275.           nextchar:=0;
  276.           internal_getopt:='?';
  277.           exit;
  278.           end;
  279.         end 
  280.       else { argument in next paramstr...  } 
  281.         begin
  282.         if pfound^.has_arg=1 then
  283.           begin
  284.           if optind<nrargs then
  285.             begin
  286.             optarg:=strpas(argv[optind]);
  287.             inc(optind);
  288.             end { required argument }
  289.           else
  290.             begin { no req argument}
  291.             if opterr then
  292.               writeln (paramstr(0),': option ',pfound^.name,' requires an argument');
  293.             nextchar:=0;
  294.             if optstring[1]=':' then
  295.               Internal_getopt:=':'
  296.             else
  297.               Internal_getopt:='?';
  298.             exit;
  299.             end;
  300.           end; 
  301.         end; { argument in next parameter end;}
  302.         nextchar:=0;
  303.       if longind<>nil then pint(longind)^:=indfound+1;
  304.       if pfound^.flag<>nil then 
  305.         begin
  306.         pfound^.flag^:=pfound^.value;
  307.         internal_getopt:=#0;
  308.         exit
  309.         end;
  310.       internal_getopt:=pfound^.value;
  311.       exit
  312.       end; { pfound<>nil }
  313.     { We didn't find it as an option }
  314.     if (not long_only) or ((currentarg[2]='-') or 
  315.                            (pos(CurrentArg[nextchar],optstring)=0)) then
  316.       begin
  317.       if opterr then
  318.         if currentarg[2]='-' then
  319.           writeln (paramstr(0),' unrecognized option "--',optname,'"')
  320.         else
  321.           writeln (paramstr(0),' unrecognized option "',currentarg[1],optname,'"');
  322.       nextchar:=0;
  323.       inc(optind);
  324.       Internal_getopt:='?';
  325.       exit;
  326.       end;              
  327.     end; { Of long options.}
  328. { We check for a short option. }
  329. temp:=pos(currentarg[nextchar],optstring);
  330. c:=currentarg[nextchar];
  331. inc (nextchar);
  332. if nextchar>length(currentarg) then 
  333.   begin
  334.   inc(optind);
  335.   nextchar:=0;
  336.   end;
  337. if (temp=0) or (c=':') then
  338.   begin
  339.   if opterr then
  340.     writeln (paramstr(0),': illegal option -- ',c);
  341.   optopt:=currentarg[nextchar-1];
  342.   internal_getopt:='?';
  343.   exit;
  344.   end;
  345. Internal_getopt:=optstring[temp];
  346. if optstring[temp+1]=':' then 
  347.   if currentarg[temp+2]=':' then
  348.     begin { optional argument }
  349.       optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  350.       nextchar:=0;
  351.     end
  352.   else
  353.     begin { required argument }
  354.     if nextchar>0 then 
  355.       begin
  356.       optarg:=copy (currentarg,nextchar,length(currentarg)-nextchar+1);
  357.       inc(optind)
  358.       end
  359.     else if (optind=nrargs) then
  360.       begin
  361.       if opterr then
  362.         writeln (paramstr(0),': option requires an argument -- ',optstring[temp]);
  363.       optopt:=optstring[temp];
  364.       if optstring[1]=':' then 
  365.         Internal_getopt:=':'
  366.       else
  367.         Internal_Getopt:='?'  
  368.       end
  369.     else
  370.       begin
  371.       optarg:=strpas(argv[optind]);
  372.       inc (optind)
  373.       end;
  374.     nextchar:=0;
  375.     end; { End of required argument}
  376. end; { End of internal getopt...}
  377.  
  378.  
  379. Function GetOpt (ShortOpts : String) : char;
  380.  
  381. begin
  382.   getopt:=internal_getopt (shortopts,nil,nil,false);
  383. end;
  384.  
  385. Function GetLongOpts (ShortOpts : String; 
  386.                       LongOpts : POption; 
  387.                       var Longind : Integer) : char;
  388. begin
  389.   getlongopts:=internal_getopt ( shortopts,longopts,@longind,true);
  390. end;
  391.  
  392. {$ifndef linux}
  393. function args : pointer;
  394.  
  395. begin
  396.   asm
  397.   movl _args,%eax
  398.   leave
  399.   ret
  400.   end ['EAX'];
  401. end;
  402. {$endif}                                                                                     
  403.                                                                                      
  404.  
  405. begin
  406.   { Needed to detect startup } 
  407.   Opterr:=true;
  408.   Optind:=0;
  409.   nrargs:=paramcount+1;
  410. {$ifndef linux}
  411.   argv:=args;
  412. {$endif}  
  413. end.     
  414.